package algo.math.evaluator.context;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import algo.math.evaluator.base.IBase;
import algo.math.evaluator.context.functions.Abs;
import algo.math.evaluator.context.functions.Max;
import algo.math.evaluator.context.functions.Min;
import algo.math.evaluator.context.operator.Addition;
import algo.math.evaluator.context.operator.Division;
import algo.math.evaluator.context.operator.Modulus;
import algo.math.evaluator.context.operator.Multiplication;
import algo.math.evaluator.context.operator.ParenthesisClose;
import algo.math.evaluator.context.operator.ParenthesisOpen;
import algo.math.evaluator.context.operator.Substraction;
import algo.math.evaluator.context.operator.interfaces.IOperator;


/**
 * Represent the context of an evaluation
 */
public class EvaluationContext {


	private static EvaluationContext _context;
	
	private List<IComponentObject> _components;
	
	private List<IComponentObject> _registeredComponents;
	
	private IBase _base;
	
	/**
	 * State of a component.
	 */
	public enum TYPE_COMPONENT
	{
		
		/** REGISTERED. */
		REGISTERED,
		
		/** AVAILABLE. */
		AVAILABLE
	}
	
	/**
	 * Get the context.
	 *
	 * @return the evaluation context
	 */
	public static EvaluationContext GetInstance()
	{
		if (null==_context)
		{
			_context = new EvaluationContext();
		}
		return _context;
	}
	
	private EvaluationContext()
	{
		_components = new ArrayList<IComponentObject>();
		_components.add(new Addition());
		_components.add(new Substraction());
		_components.add(new Division());
		_components.add(new Multiplication());
		_components.add(new Modulus());
		_components.add(new ParenthesisClose());
		_components.add(new ParenthesisOpen());
		_components.add(new Max());
		_components.add(new Min());
		_components.add(new Abs());
		
		_registeredComponents = new ArrayList<IComponentObject>();
		
		for (IComponentObject component : _components) {
			_registeredComponents.add(component);
		}
	}

	/**
	 * Add component.
	 *
	 * @param object the object
	 */
	public void addComponent (IComponentObject object)
	{
		if (!_registeredComponents.contains(object))
			_registeredComponents.add(object);
			
	}
	
	/**
	 * Gets separators.
	 *
	 * @return the separators
	 */
	public List<Character> getSeparators()
	{
		List<Character> l = new ArrayList<Character>();
		for (char c : IBase.SEPARATOR) {
			l.add(c);
		}
		
		return l;
	}
	
	/**
	 * Gets available components.
	 *
	 * @return the available components
	 */
	public List<IComponentObject> getAvailableComponents ()
	{
		return _components;
	}
	
	/**
	 * Reset components.
	 */
	public void resetComponents ()
	{
		_registeredComponents =_components;
	}
	
	/**
	 * Sets the base.
	 *
	 * @param base the new base
	 */
	public void setBase(IBase base)
	{
		_base = base;
	}
	
	/**
	 * Gets the base.
	 *
	 * @return the base
	 */
	public IBase getBase()
	{
		return _base;
	}
	
	/**
	 * Removes the component.
	 *
	 * @param object the object
	 */
	public void removeComponent (IComponentObject object)
	{
		if (_registeredComponents.contains(object))
			_registeredComponents.remove(object);
	}
	
	/**
	 * Get the registered operators label
	 *
	 */
	public List<String> registredOperators() {
		List<IComponentObject> l = getComponents(IOperator.class,TYPE_COMPONENT.REGISTERED);
		List<String> operator = new ArrayList<String>();
		for (IComponentObject operators: l) {
			operator.add(((IOperator)operators).getName());
	}
		return operator;
	}
	
	/**
	 * Get the registered operators symbol.
	 *
	 */
	public List<String> registredOperatorsSymbols() {
		List<IComponentObject> l = getComponents(IOperator.class,TYPE_COMPONENT.REGISTERED);
		List<String> operator = new ArrayList<String>();
		for (IComponentObject operators: l) {
			operator.add(((IOperator)operators).getValue());
	}
		return operator;
	}
	
	/**
	 * Gets the components.
	 *
	 * @param typeOfComponent the type of components
	 * @param type the state of the components
	 */
	public List<IComponentObject> getComponents(Type typeOfComponent, TYPE_COMPONENT type)
	{
		List<IComponentObject> c = new ArrayList<IComponentObject>();
		
		for (IComponentObject component : getComponents(type)) {
			if (component.getComponentType()==typeOfComponent)
			{
				c.add(component);
			}
		}
		
		return c;
	}

	/**
	 * Gets the components.
	 *
	 * @param type the type of the components
	 */
	public List<IComponentObject> getComponents(TYPE_COMPONENT type)
	{
		if (type== TYPE_COMPONENT.REGISTERED)
		{
			return _registeredComponents;
		}
		else
		{
			return _components;
		}
		
	}
	
	/**
	 * Gets the component by id.
	 *
	 */
	public IComponentObject getComponentById (Type type, String id)
	{	
		for (IComponentObject component :getComponents(type,TYPE_COMPONENT.REGISTERED)) {
			if (component.getComponentId().equals(id))
			{
				return component;
			}
		}
		return null;
	}
	
	/**
	 * Gets the component by name.
	 *
	 */
	public IComponentObject getComponentByName (Type type, String name)
	{	
		for (IComponentObject component :getComponents(type,TYPE_COMPONENT.REGISTERED)) {
			if (component.getComponentName().equals(name))
			{
				return component;
			}
		}
		return null;
	}
	
	/**
	 * Gets the component by name.
	 *
	 */
	public IComponentObject getComponentByName (String name)
	{	
		for (IComponentObject component :getComponents(TYPE_COMPONENT.REGISTERED)) {
			if (component.getComponentName().equals(name))
			{
				return component;
			}
		}
		return null;
	}
	
	

}
